﻿namespace NTLib.Core.Component
{
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Reflection;
    using System.Text;
    using System.Threading;
    using System.Threading.Tasks;
    using NTLib.Core.Threading;
    using System.Diagnostics;


    /// <summary>
    /// NOT IMPLEMENTED
    /// </summary>
    public static class AutoLoader
    {
        #region Fields

        //private static HashSet<Assembly> s_assemblyLoaded;
        //private static SemaphoreSlim s_locker;
        //private static Dictionary<Type, WeakReference<object>> s_itemLoaded;
        //private static Dictionary<Type, List<object>> s_autoLoadInstance;
        //private static Dictionary<Type, MethodInfo> s_loadMethods;
        //private static Dictionary<Type, MethodInfo> s_unloadMethods;
        //private static MethodInfo s_load;
        //private static MethodInfo s_unload;

        #endregion

        #region Ctor

        /// <summary>
        /// Initializes the <see cref="AutoLoader"/> class.
        /// </summary>
        static AutoLoader()
        {
            //s_assemblyLoaded = new HashSet<Assembly>();
            //s_locker = new SemaphoreSlim(1);
            //s_autoLoadInstance = new Dictionary<Type, List<object>>();
            //s_loadMethods = new Dictionary<Type, MethodInfo>();
            //s_unloadMethods = new Dictionary<Type, MethodInfo>();
            //s_itemLoaded = new Dictionary<Type, WeakReference<object>>();

            //s_load = typeof(AutoLoader).GetRuntimeMethods().FirstOrDefault(m => m.Name == "Load");
            //s_unload = typeof(AutoLoader).GetRuntimeMethods().FirstOrDefault(m => m.Name == "UnLoad");

            //Debug.Assert(s_load != null);
            //Debug.Assert(s_unload != null);
        }

        #endregion

        #region Methods

        /// <summary>
        /// Loads the assembly.
        /// </summary>
        /// <param name="assembly">The assembly.</param>
        public static void LoadAssembly(Assembly assembly)
        {
            //using (s_locker.ScopeWait())
            //{
            //    if (s_assemblyLoaded.Contains(assembly))
            //        return;

            //    s_assemblyLoaded.Add(assembly);

            //    var iautoload = typeof(IComponentLoader<>);
            //    // Get type that inherit au IAutoLoad<T> and that have the attribute AutoLoaClass with the type associate
            //    // Key => type that load
            //    // toLoad => type of the object the 'key' could load
            //    var autoInfo = (from t in assembly.ExportedTypes
            //                    let info = t.GetTypeInfo()
            //                    let attr = info.GetCustomAttributes<AutoLoadClassAttribute>().ToArray()
            //                    where attr != null && attr.Length > 0 && attr.All((a) =>
            //                                                                      iautoload.MakeGenericType(a.TypeAutoLoad)
            //                                                                      .GetTypeInfo()
            //                                                                      .IsAssignableFrom(info))
            //                    select new { type = t, toLoad = attr.Select(a => a.TypeAutoLoad) });

            //    var typeToLoad = autoInfo.Aggregate(new List<Type>(), (l, info) =>
            //    {
            //        l.AddRange(info.toLoad);
            //        return l;
            //    });

            //    typeToLoad.AddRange(s_autoLoadInstance.Keys);

            //    // typeToLoad contains all the type that could be loaded.
            //    typeToLoad = typeToLoad.Distinct().ToList();

            //    // Get all the instance of the object that is in the new assembly and create and instance to be loaded.
            //    var instances = (from t in assembly.ExportedTypes
            //                     where typeToLoad.Any(toLoadType => toLoadType.GetTypeInfo().IsAssignableFrom(t.GetTypeInfo()))
            //                     select t)
            //                     .Distinct()
            //                     .Select(t => new { Key = t, Value = Activator.CreateInstance(t) }).ToDictionary(k => k.Key, v => v.Value);

            //    // Look in the AutoLoader object already create and filled them with the new object that come from the new assembly
            //    foreach (var autoLoadInstance in s_autoLoadInstance)
            //    {
            //        object instance = null;
            //        if (instances.TryGetValue(autoLoadInstance.Key, out instance))
            //        {
            //            foreach (var loader in autoLoadInstance.Value)
            //            {
            //                ObjectLoad(autoLoadInstance.Key, instance, loader);
            //            }
            //        }
            //    }

            //    // insert the new element in the static storage
            //    foreach (var instance in instances)
            //    {
            //        s_itemLoaded.Add(instance.GetType(), new WeakReference<object>(instance));
            //    }

            //    // Create and filled the new autoloader object
            //    foreach (var newAutoLoader in autoInfo)
            //    {
            //        var newLoaderInstance = Activator.CreateInstance(newAutoLoader.type);

            //        foreach (var toLoad in newAutoLoader.toLoad)
            //        {
            //            WeakReference<object> wInstance = null;
            //            if (s_itemLoaded.TryGetValue(toLoad, out wInstance))
            //            {
            //                object instance = null;
            //                if (!wInstance.TryGetTarget(out instance))
            //                {
            //                    // Resurect the instance if no body use it
            //                    instance = Activator.CreateInstance(toLoad);
            //                    wInstance.SetTarget(instance);
            //                }

            //                ObjectLoad(toLoad, instance, newLoaderInstance);
            //            }
            //        }
            //    }
            //}
        }

        /// <summary>
        /// Unloads the assembly.
        /// </summary>
        /// <param name="assembly">The assembly.</param>
        public static void UnloadAssembly(Assembly assembly)
        {

        }

        private static void ObjectLoad(Type Key, object instance, object loader)
        {
            //MethodInfo infoMthd = null;
            //if (s_loadMethods.TryGetValue(Key, out infoMthd))
            //{
            //    infoMthd = s_load.MakeGenericMethod(Key);
            //    s_loadMethods.Add(Key, infoMthd);
            //}
            //infoMthd.Invoke(null, new object[] { instance, loader });
        }

        private static void Load<T>(T instance, IComponentLoader<T> loader)
            where T : IComponent
        {
            //loader.Load(instance);
        }

        private static void UnLoad<T>(T instance, IComponentLoader<T> loader)
            where T : IComponent
        {
            //loader.UnLoad(instance);
        }

        #endregion
    }
}
